package study.alex.resumedb.gae;


import com.vaadin.addon.jpacontainer.JPAContainerFactory;
import com.vaadin.data.Item;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.validator.IntegerRangeValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.ui.*;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;
import study.alex.resumedb.gae.model.GoogleDriveResumeBlob;
import study.alex.resumedb.gae.model.Resume;

import javax.persistence.EntityManager;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.logging.Logger;

/**
 * Copyright 2009-2013 Oy Vaadin Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@SuppressWarnings("serial")
public class ResumeEditor extends Window implements Serializable {

    private final static Logger log = Logger.getLogger(ResumeEditor.class.getName());

    private final static String CACHE_PREFIX = "_rblob_";

    private final Item resumeItem;
    private final FieldGroup editorForm;
    private final Button saveButton;
    private final Button cancelButton;
    private final FormLayout form;
    private final ResumeEditor thisComponent;

    public void saveEnable(boolean flag) {
        if (saveButton != null) saveButton.setEnabled(flag);
    }

    public ResumeEditor(Item item, Resume resume) {
        thisComponent = this;
        this.resumeItem = item;
        editorForm = new FieldGroup(resumeItem);
        editorForm.setBuffered(true);
        final TextField posField = editorForm.buildAndBind(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("position"), "position", TextField.class);
        posField.setRequired(true);
        posField.setRequiredError(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("you.must.set.a.position"));
        posField.addValidator(new RegexpValidator("^(\\S+-?\\S*)(\\s+\\S+-?\\S*)*$", ((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("you.must.set.a.position")));
//        posField.addValidator(new NullValidator(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("empty.position.is.not.allowed"), false));

        posField.setNullSettingAllowed(true);
        posField.setNullRepresentation("");

        final TextField salField = editorForm.buildAndBind(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("salary"), "salary", TextField.class);
        salField.addValidator(new IntegerRangeValidator(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("you.must.set.a.salary.from.0.to.1m"), 0, 1000000));
        form = new FormLayout(posField, salField);
        form.setSizeUndefined();

        final Upload fileUpload;
        BlobUploadReceiver receiver = new BlobUploadReceiver(resume, this);

        fileUpload = new Upload(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("resume.file"), receiver);
        fileUpload.addStartedListener(receiver);
        fileUpload.addFinishedListener(receiver);
        fileUpload.addSucceededListener(receiver);
        fileUpload.addFailedListener(receiver);

        receiver.addListener(new BlobUploadReceiver.ReceivedListener() {
            @Override
            public void blobSucceeded(BlobUploadReceiver.ReceiverEvent event) {
                GoogleDriveResumeBlob newBlob = (GoogleDriveResumeBlob) event.getReceivedBlob();
                Logger.getLogger(ResumeEditor.class.getName()).fine("Blob type in event: " + newBlob.getType());
                if (newBlob != null) {
                    EntityManager em = JPAContainerFactory.createEntityManagerForPersistenceUnit(EMF.PERSISTENCE_UNIT);
                    newBlob = em.merge(newBlob);
                    em.close();

                    Logger.getLogger(ResumeEditor.class.getName()).fine("Blob Id after persist: " + newBlob.getId());
                    Logger.getLogger(ResumeEditor.class.getName()).fine("phase 2");

                    Cache cache;

                    try {
                        CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
                        cache = cacheFactory.createCache(Collections.emptyMap());
                    } catch (CacheException e) {
                        log.fine("Can not create memcache. \n" + e.toString());
                        return;
                    }

                    String user = newBlob.getUserId();
                    String resId = (String) resumeItem.getItemProperty("id").getValue();
                    String key = CACHE_PREFIX + user + resId;

                    cache.put(key, newBlob);

                    //                    Logger.getLogger(ResumeEditor.class.getName()).fine("Now blobId is " + blobProperty.getValue().getId());
                    Logger.getLogger(ResumeEditor.class.getName()).info("Put blob in cache.  Key: " + key);
                }
            }
        });

        fileUpload.setButtonCaption(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("upload"));
        fileUpload.setSizeUndefined();
        fileUpload.setImmediate(true);

        if (resumeItem instanceof BeanItem) fileUpload.setEnabled(true);
        else fileUpload.setEnabled(false);

        saveButton = new Button(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("save"));
        saveButton.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent event) {
                try {
                    editorForm.commit();
                    if (resumeItem instanceof BeanItem) {
                        Cache cache;

                        try {
                            CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
                            cache = cacheFactory.createCache(Collections.emptyMap());
                        } catch (CacheException e) {
                            log.fine("Can not create memcache.\n" + e.toString());
                            return;
                        }

                        String user = (String)resumeItem.getItemProperty("userId").getValue();
                        String resId = (String)resumeItem.getItemProperty("id").getValue();
                        String key = CACHE_PREFIX + user + resId;

                        log.fine("Trying to get blob from memchache.  Key: " + key);

                        GoogleDriveResumeBlob bl = (GoogleDriveResumeBlob)cache.get(key);

                        log.fine("Got blob from memcache: " + bl);

                        if (bl != null) {
                            Logger.getLogger(ResumeEditor.class.getName()).fine("Blob type before setValue: " + bl.getType());
                            resumeItem.getItemProperty("body").setValue(bl);
                            Logger.getLogger(ResumeEditor.class.getName()).fine("Blob type after setValue: " + ((ResumeBlob)resumeItem.getItemProperty("body").getValue()).getType());
                            cache.remove(key);
                        } else {
                            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("can.t.get.resume.file.information.storing.resume.without.resume.file"), Notification.Type.WARNING_MESSAGE);
                        }
                    }
                    Logger.getLogger(ResumeEditor.class.getName()).fine("Blob object before fire event: " + resumeItem.getItemProperty("body").getValue());
                    fireEvent(new EditorSavedEvent(thisComponent, resumeItem));
                } catch (FieldGroup.CommitException e) {
                    Logger.getLogger(ResumeEditor.class.getName()).fine("Error when commit data");
                    return;
                }
                thisComponent.close();
            }
        });

        cancelButton = new Button(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("cancel"));
        cancelButton.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent event) {
                editorForm.discard();
                thisComponent.close();
            }
        });

        final HorizontalLayout footer = new HorizontalLayout(saveButton, cancelButton);
        footer.setSizeUndefined();
        footer.setSpacing(true);
        VerticalLayout panel = new VerticalLayout(form, fileUpload, footer);
        panel.setSizeFull();
        panel.setComponentAlignment(form, Alignment.TOP_CENTER);
        panel.setComponentAlignment(fileUpload, Alignment.MIDDLE_CENTER);
        panel.setComponentAlignment(footer, Alignment.BOTTOM_CENTER);
        panel.setSpacing(true);
        panel.setMargin(true);
        setContent(panel);
        setCaption(buildCaption());
        setWidth("25%");
        setHeight("35%");
        center();
        setModal(true);
    }

    /**
     * @return the caption of the editor window
     */
    private String buildCaption() {
        if (resumeItem instanceof BeanItem) {
            return String.format(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("add.new.resume"));
        } else {
            return String.format(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("update.resume"));
        }
    }


    public void addListener(EditorSavedListener listener) {
        try {
            Method method = EditorSavedListener.class.getDeclaredMethod(
                    "editorSaved", new Class[] { EditorSavedEvent.class });
            addListener(EditorSavedEvent.class, listener, method);
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error, editor saved method not found");
        }
    }

    public void removeListener(EditorSavedListener listener) {
        removeListener(EditorSavedEvent.class, listener);
    }

    public static class EditorSavedEvent extends Component.Event {

        private Item savedItem;

        public EditorSavedEvent(Component source, Item savedItem) {
            super(source);
            this.savedItem = savedItem;
        }

        public Item getSavedItem() {
            return savedItem;
        }
    }

    public interface EditorSavedListener extends Serializable {
        public void editorSaved(EditorSavedEvent event);
    }

}